#pragma once
#include "Vector2.hpp"
#include "Array.hpp"

namespace zzz {
template <typename T>
struct Array<2, T> : public ArrayBase<2, T>
{
  using ArrayBase<2, T>::v;
  using ArrayBase<2, T>::subsizes;
public:
  //constructor
  Array(void){}
  Array(const zuint r, const zuint c):ArrayBase<2, T>(Vector2ui(r, c)){}
  explicit Array(const VectorBase<2,zuint> &size):ArrayBase<2, T>(size){}
  Array(const T *data, const zuint r, const zuint c):ArrayBase<2, T>(data, Vector2ui(r, c)){}
  Array(const T *data, const VectorBase<2,zuint> &size):ArrayBase<2, T>(data, size){}
  Array(const Array<2, T>& a):ArrayBase<2, T>(a) {}
  Array(const ArrayBase<2, T>& a):ArrayBase<2, T>(a) {}

  //assign
  inline const Array<2, T> &operator=(const Array<2, T>& a){ArrayBase<2, T>::operator =(a); return *this;}
  using ArrayBase<2, T>::operator=;

  //reference
  using ArrayBase<2, T>::operator();
  using ArrayBase<2, T>::At;
  const T& At(const zuint r, const zuint c) const{
    ZRCHECK_LT(r, Size(0));
    ZRCHECK_LT(c, Size(1));
    return At(ToIndex(r, c));
  }
  T& At(const zuint r, const zuint c) {
    ZRCHECK_LT(r, Size(0));
    ZRCHECK_LT(c, Size(1));
    return At(ToIndex(r, c));
  }
  const T& operator()(const zuint r, const zuint c) const{return At(r, c);}
  T& operator()(const zuint r, const zuint c) {return At(r, c);}

  T Interpolate(const double r, const double c) const {return ArrayBase<2, T>::Interpolate(Vector2d(r, c));}
  using ArrayBase<2, T>::Interpolate;

  zuint ToIndex(const zuint r, const zuint c) const {return r*subsizes[0] + c*subsizes[1];}
  using ArrayBase<2, T>::ToIndex;

  bool CheckIndex(const zuint r, const zuint c) const {return ArrayBase<2, T>::CheckIndex(Vector2ui(r, c));}
  using ArrayBase<2, T>::CheckIndex;

  void SetSize(const zuint r, const zuint c) {ArrayBase<2, T>::SetSize(Vector2ui(r, c));}
  using ArrayBase<2, T>::SetSize;

  void Fill(const zuint r, const zuint c, const T &a) {
    SetSize(r, c);
    Fill(a);
  }
  using ArrayBase<2, T>::Fill;

};

typedef Array<2,zint8> Array2i8;
typedef Array<2,zuint8> Array2ui8;
typedef Array<2,zint16> Array2i16;
typedef Array<2,zuint8> Array2ui16;
typedef Array<2,zint32> Array2i32;
typedef Array<2,zuint32> Array2ui32;
typedef Array<2,zint64> Array2i64;
typedef Array<2,zuint64> Array2ui64;
typedef Array<2,zfloat32> Array2f32;
typedef Array<2,zfloat64> Array2f64;

typedef Array<2,char> Array2c;
typedef Array<2,zuchar> Array2uc;
typedef Array<2,short> Array2s;
typedef Array<2,zushort> Array2us;
typedef Array<2,int> Array2i;
typedef Array<2,zuint> Array2ui;
typedef Array<2,float> Array2f;
typedef Array<2,double> Array2d;
};  // namespace zzz